<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/7/6 0006
 * Time: 3:11
 */

namespace api\storage\service;


use api\storage\common\Constant;
use api\storage\model\SeatModel;
use api\storage\model\SmtMaterielModel;
use api\storage\model\SmtOutflowModel;

use service\file\Lock;
use service\logger\LoggerFactory;
use think\Db;
use think\image\Exception;
use think\Process;
use api\storage\model\TransferModel;
use api\storage\model\ReelsModel;
use api\storage\model\FeedDetailModel;
use think\Request;


class FeedOutputService extends FeedService
{
    protected $feedDetailModel;
    protected $transferModel;
    protected $reelModel;
    protected $storageUuid;
    protected $smtMaterielModel;
    protected $smtOutflowModel;
    protected $seatModel;

    public function __construct(Request $request)
    {
        parent::__construct($request);
        $this->transferModel = new TransferModel();
        $this->reelModel = new ReelsModel();
        $this->feedDetailModel = new FeedDetailModel();
        $this->smtMaterielModel = new SmtMaterielModel();
        $this->smtOutflowModel = new SmtOutflowModel();
        $this->seatModel = new SeatModel();
        $this->logger = LoggerFactory::getLogger('feed');

    }

    public function moveMateriel($feedId, $reelId, $storageUuid = "184928afb9e35f3ff421da05fc3f917f")
    {
        $this->outPutFile($feedId, $storageUuid);
        if ($this->isEmpty($this->file)) {
            return $this->resultTemplate(1);
        }
        $data = $this->jsonDecode($this->file->read());


//todo:


    }

    /****
     * @param $feedId
     * @param $process
     * @param $reelId
     * @param $name
     * @param string $storageUuid
     * @return array
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException 从货架中出库
     */
    public function outputFromShelve($feedId, $process, $reelId, $name, $storageUuid = "184928afb9e35f3ff421da05fc3f917f")
    {
        $this->logger->info("用户{$name}进行货架出库 工单:{$feedId} reelId：{$reelId}");
        $feedPath = $this->getFeedPath($feedId, $storageUuid);//获取当前物料号对应的feedPreparePosition文件地址
        $fp = fopen($feedPath, 'r');//以读写的方式打开
        flock($fp, LOCK_EX);//给文件加堵塞锁
        $data = json_decode(fread($fp, filesize($feedPath)), true);//获取feedPreparePosition文件数据
        fclose($fp);
        if (empty($data)) {
            return $this->resultTemplate(6);
        }

        if ($data['process'] != $process) {//如果当前文件流程与pda上的流程不同，说明发生了改变
            flock($fp, LOCK_UN);
            $this->logger->warn("reelId：{$reelId} 出库失败，流程已被其他Pda改变");
            return $this->resultTemplate(2, $data);
        }
        $reelObject = $this->reelModel->findReelByIdAndUuid($reelId, $storageUuid, 0);//根据reelId和仓库uuid查找status为0的仓库（之所以是0是因为在分配首盘物料的时候设置成了0；

        if (empty($reelObject)) {
            flock($fp, LOCK_UN);
            $this->logger->warn("{$reelId}不存在仓库中");
            return $this->resultTemplate(3, $data);//这个说明仓库中找不到这条记录了，出库以后会删除这条记录，所以可能是连续触发接口导致的。
        }
        $reel = $reelObject->toArray();
        $shelveSeatCode = $reel['seat_code'];
        unset($reel['time']);
        unset($reel['seat_code']);
        unset($reel['container_code']);//去掉这些字段是因为transfer表不需要这些字段
        $reel['status'] = 0;//表示该盘是中转区的待上机器的物料
        foreach ($data['reelsData'] as &$item) {/*找到该盘料所对应的reelsData，将出库isOutput修改为true*/
            if ($item['reelId'] == $reel['reel_id']) {
                $where['machine_id'] = $item['destinationMachine'];//该盘料将要放置的机器
                $where['seat_code'] = $item['destinationSeatCode'];
                $item['isOutput'] = true;
                break;
            }
        }
        $machineSeat = $where['machine_id'] . "#" . $where['seat_code'];//对其进行拼接，以此来查找reelsData所对应的feedStationData
        $where['feed_id'] = $feedId;
        $feedDetail = $this->feedDetailModel->findFeedDetail($where)->toArray();

        $feedDetail['sent_count'] += $reel['count'];//对料站细表的已发数量进行一个增加

        foreach ($data['feedStationData'] as &$item) {//遍历feedStationData，修改对应的已发数量sent_count
            if ($item['machineSeat'] == $machineSeat) {
                $item['sentCount'] = $feedDetail['sent_count'];
                break;
            }
        }

        $reel['feed_detail_uuid'] = $feedDetail['uuid'];
        $this->transferModel->add($reel);//增加transfer表数据
        $this->reelModel->deleteByUuidList($reel['uuid']);//删除对应的reel


        $fp = fopen($feedPath, 'w');
        fwrite($fp, json_encode($data));//将修改后的数据写回outputPrepare文件中
        flock($fp, LOCK_UN);
        fclose($fp);
        $outputOver = $this->checkFinish($data['reelsData'], $feedId);
        if ($outputOver){
            unlink($feedPath);

        }


        $subMaterielId = $feedDetail['sub_materiel_id'];
        $materielData = $this->smtMaterielModel->findByLock(['sub_materiel_id' => $subMaterielId])->toArray();
        $materielData['total_num'] = $materielData['total_num'] - $reel['count'];//当前物料数量
        $this->smtMaterielModel->updateMaterielWithLock(['total_num' => $materielData['total_num']], ['sub_materiel_id' => $subMaterielId]);
        $SmtOutflow = [//写smt出库流水
            'uuid' => md5(create_uuid()),
            'reel_id' => $reel['reel_id'],
            'count' => $reel['count'],
            'sub_materiel_id' => $reel['sub_materiel_id'],
            'feed_id' => $feedId,
            'machine_id' => $where['machine_id'],
            'seat_code' => $where['seat_code'],//这个是指机台站位，$shelveSeatCode才是指货架站位
            'storage_uuid' => $storageUuid,
            'user_login' => $name,
            'remark' => '货架出库'
        ];
        $feedDetailCondition = ['feed_id' => $feedId, 'machine_id' => $where['machine_id'], 'seat_code' => $where['seat_code']];
        $this->smtOutflowModel->addOutFlow($SmtOutflow);//增加smt出库流水

        $this->feedDetailModel->updateFeedDetail($feedDetailCondition, ['sent_count' => $feedDetail['sent_count']]);
        $this->seatModel->updateIsNotUsed($shelveSeatCode,$storageUuid);
        if ($outputOver){
            $this->logger->info("全部出库完毕，工单{$feedId}出库已结束");
            return $this->resultTemplate(6);
        }
        $this->logger->info("工单：{$feedId} 物料条码：{$reelId} 出库成功 出库站位：{$shelveSeatCode}");
        return $this->resultTemplate(1, $data);

    }

    public function cutMaterielFromMachine()
    {
//todo:
    }

    /****
     * @param $feedId
     * @param $process
     * @param $username
     * @param string $storageUuid
     * @return array
     * 流程1是从中转区换枪的流程
     * 流程2是从货架上出库的流程
     * 流程3是从机器上剪料的流程
     *
     * 出库时询问是否从中转区获取物料
     * 如果选择否，则会调用这个接口，将process从流程1切换成流程2，推物料的首盘。
     * 选择是，进行中转站上料，结束以后也会切换成这个接口
     * @throws \think\exception\PDOException
     */
    public function changeProcess($feedId, $process, $username,$storageUuid = "184928afb9e35f3ff421da05fc3f917f")
    {
        $feedPath = $this->getFeedPath($feedId, $storageUuid);//获取当前物料号对应的feedPreparePosition文件地址
        $fp = fopen($feedPath, 'r');//以读写的方式打开
        flock($fp, LOCK_EX);//给文件加堵塞锁
        $data = json_decode(fread($fp, filesize($feedPath)), true);//获取文件数据
        fclose($fp);


        if (empty($data)) {//如果文件不存在
            flock($fp, LOCK_UN);//解锁
            $this->resultTemplate(3);
        }
        if ($data['process'] == $process) {//当前流程是否与文件保存的相同
            flock($fp, LOCK_UN);
            return $this->resultTemplate(2, $data);
        } elseif ($data['process'] == 1 && $process == 2) {//当前为流程1，准备进入流程2
            $fp = fopen($feedPath, "w");
            $this->logger->info("{$username}首次进入货架出库流程，为{$feedId}物料寻找首盘:");
            $setData = $this->setFirstReel($data, $storageUuid,$feedId);//为货架出库分配首盘物料
            $setData['process'] = 2;//进入货架出库流程
            fwrite($fp, json_encode($setData));
            fclose($fp);
            flock($fp, LOCK_UN);
            $this->logger->info("工单{$feedId}进入货架出库流程");
            return $this->resultTemplate(1, $setData);
        } elseif ($data['process'] == 2 && $process == 3) {//进入从机器上获取物料的阶段。
            //todo:
        }
        fclose($fp);
    }

    /****
     * @param $feedId
     * @param $username
     * @param string $storageUuid
     * @return array
     * 刷新页面
     */
    public function refresh($feedId, $username,$storageUuid = "184928afb9e35f3ff421da05fc3f917f")
    {

        $this->outPutFile($feedId, $storageUuid);//该函数继承自FeedService ，实例化outputPrepare文件
        $file = $this->jsonDecode($this->file->read());//读取outputPrepare文件

        if ($this->isEmpty($file)) {
            //文件不存在
            return $this->resultTemplate(4);
        }
        if ($file['process'] == Constant::FEED_OUTPUT_PROCESS_MOVE_MATERIEL) {//说明当前是移料流程，返回数据
            return $this->resultTemplate(1, $file);
        }
        if ($file['process'] == Constant::FEED_OUTPUT_PROCESS_SHELVE) {//说明当前是货架出库流程，返回数据
            $this->logger->info("{$username}进入多人货架出库");
            $isFinish = $this->checkFinish($file['reelsData'], $feedId);
            if ($isFinish){
                $this->file->delete();
                return $this->resultTemplate(4);
            }
            return $this->resultTemplate(2, $file);
        }
        if ($file['process'] == Constant::FEED_OUTPUT_PROCESS_MACHINE) {//说明当前是机器出库流程，返回数据
            return $this->resultTemplate(3, $file);
        }
    }

    /***
     * @param $process
     * @param $reelId
     * @param string $storageUuid
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @throws \think\Exception
     * 错料归还
     */
    public function mismatchReturn($process, $reelId, $storageUuid = "184928afb9e35f3ff421da05fc3f917f")
    {
        if ($process == 2) {
            $result = $this->reelModel->findReelByIdAndUuid($reelId, $storageUuid);
            if (!empty($result)) {
                $data = [
                    'materiel' => $result['sub_materiel_id'],
                    'seatCode' => $result['seat_code'],
                    'containerCode' => $result['container_code']
                ];
                $this->logger->warn("扫描了错误的物料，请扫码归还。错料reelId：{$reelId} 物料类型：{$result['sub_materiel_id']} 错料站位：{$result['seat_code']} 错料容器号：{$result['container_code']}");
                return $this->resultTemplate(1, $data);//扫描了错误的物料，请扫码归还
            } else {
                $this->logger->warn("物料{$reelId}不存在于库中，请直接归还。");
                return $this->resultTemplate(2);//扫描了错误的物料，请直接归还
            }
        }
        return $this->resultTemplate(2);
    }

    /***
     * @param $data
     * @param $storageUuid
     * @param $feedId
     * @return mixed
     * 为货架出库分配首盘物料
     * @throws \think\exception\PDOException
     * @throws \Exception
     */
    public function setFirstReel($data, $storageUuid, $feedId)
    {


        $feedStationData = $data['feedStationData'];
        foreach ($feedStationData as $key => $item) {
            if ($item['isFirst'] == true) {
                $this->reelModel->startTrans();
                try {
                    $result = $this->reelModel->findReelWithLock($item['materiel'], $storageUuid);//根据物料号获取reel
                    if (!empty($result)) {
                        $reelData = $result->toArray();
                        $this->reelModel->updateReel($reelData['reel_id'], $storageUuid, ['status' => 0]);//将获取到的物料状态设置为0
                        $this->reelModel->commit();
                        $this->logger->info("工单：{$feedId} 机器号：{$item['machineId']} 站位号:{$item['seatCode']} 成功获取首盘,reelId:{$reelData['reel_id']}");
                        $data['reelsData'][] = [
                            'destinationMachine' => $item['machineId'],
                            'destinationSeatCode' => $item['seatCode'],
                            'reelId' => $reelData['reel_id'],
                            'seatCode' => $reelData['seat_code'],
                            'containerCode' => $reelData['container_code'],
                            'count' => $reelData['count'],//物料数量
                            'isOutput' => false,
                            'totalCount' => $feedStationData[$key]['totalCount'],
                            'machineSeat' => $item['machineId'] . "#" . $item['seatCode'],
                            'materielId' => $item['materiel']

                        ];
                        $data['feedStationData'][$key]['isFirst'] = false;
                    }else{
                        $this->reelModel->commit();
                        $this->logger->info("工单：{$feedId} 机器号：{$item['machineId']} 站位号:{$item['seatCode']} 为找到可用盘料");
                    }

                } catch (\Exception $e) {
                    $this->reelModel->rollback();
                }


            }
        }

        return $data;
    }


    public function getFeedPath($feedId, $storageUuid)
    {
        $this->storageUuid = $storageUuid;
        $feedPath = ROOT_PATH . Constant::RECORD_FEED_PREPARE_POSITION . $storageUuid . '/' . $feedId . Constant::RECORD_FILE_SUFFIX;
        return $feedPath;
    }

    /***
     * @param $data
     * @param $feedId
     * @return bool
     * 检验是否完成
     */
    public function checkFinish($data, $feedId)
    {
        foreach ($data as $item) {
            if ($item['isOutput'] == false) {//如果存在未出库
                return false;//返回false
            }
        }
        $this->feedStationModel->updateFeed(['feed_id' => $feedId],['status' => '1']);//设置工单状态为正在生产
        return true;//全部出库则为true
    }

}